/*
Copyright 2008, 2009 Joonas Lehtolahti

This file is part of Openfire-C.

Openfire-C is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Openfire-C is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with Openfire-C.  If not, see <http://www.gnu.org/licenses/>.
*/


#include "packet_creation/XfirePacketsClientFriendsList.h"

/**
 * XfireAnalyze_h* (XFINST inst, struct XfirePacket *packet)
 *   Internal handlers for different Xfire packets
 * 
 * Parameters:
 *   inst    the Xfire instance
 *   packet  the packet data
 * 
 * Returns:
 *   XFIH_FATAL           external handler returned XFNH_FATAL
 *  The following return values may be combined using a bitwise OR fashion:
 *   XFIH_PENDING         all external handlers were not called
 *   XFIH_HANDLED         data handled internally = no XFNH_NOHANDLE seen
 *   XFIH_UNKNOWN         data unknown for internal handling - PROTOCOL CHANGES!!
 *   XFIH_EXTERNAL        data handled externally
 *   XFIH_NONFATAL        internal handler encountered non-fatal error
 */
int XfireAnalyze_hFriends(XFINST inst, struct XfirePacket *packet) {
	/* New friends on friends list
	 * http://xfireplus.com/xfwiki/index.php/Packet_83_00
	 *
	 * NOW USES SINGLE LIST CALLBACK
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT LinkedList*
	#define _XFABP friend_list
	
	LinkedList *username_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"friends");
	LinkedList *nickname_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"nick");
	LinkedList *userid_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"userid");
	
	if (username_list && nickname_list && userid_list) {
		if (username_list->nodes == nickname_list->nodes && nickname_list->nodes && userid_list->nodes) {
			LinkedList *friend_list = LLcreate();
			
			ListNode *username_node = username_list->first;
			ListNode *nickname_node = nickname_list->first;
			ListNode *userid_node = userid_list->first;
			struct XfFriend *xff;
			TreeNode *op_result;
			
			while (userid_node) {
				xff = XfireFriend();
				xff->userid = *(unsigned int*)(userid_node->x);
				xff->username = n0scopy((non0string*)(username_node->x));
				xff->nickname = n0scopy((non0string*)(nickname_node->x));
				LLaddLast(friend_list, xff);
				
				NEXT_NODE(username_node);
				NEXT_NODE(nickname_node);
				NEXT_NODE(userid_node);
			}
			
			/* include external handlers calling */
			#include "XfirePacketAnalyzeExt.inc"
			
			/* internal handling */
			if (retval & XFIH_HANDLED) {
				ListNode *friend_node = friend_list->first;
				while (friend_node) {
					xff = (struct XfFriend*)friend_node->x;
					op_result = BTreeFind(inst->other_people, xff);
					if (op_result) {
						/* We already have this user in "other people",
						 * clear the new struct but steal username and nickname */
						non0string *username = xff->username;
						non0string *nickname = xff->nickname;
						xff->username = NULL;
						xff->nickname = NULL;
						FreeXfireFriend(xff);
						/* Get the existing friend info and update it */
						xff = (struct XfFriend *)op_result->x;
						n0sfree(xff->username);
						n0sfree(xff->nickname);
						xff->username = username;
						xff->nickname = nickname;
						/* Since the person is now a friend, remove from others */
						BTreeDel(inst->other_people, op_result);
					}
					BTreeAdd(inst->friends, xff);
					
					if (inst->self != xff && inst->self->userid == xff->userid) {
						/* Me and myself */
						FreeXfireFriendExtData(xff->extdata);
						xff->extdata = inst->self->extdata;
						inst->self->extdata = NULL;
						FreeXfireFriend(inst->self);
						inst->self = xff;
					}
					
					NEXT_NODE(friend_node);
				}
				LLdelete(friend_list, NULL);
			}
			else {
				/* Delete the new friends :( */
				LLdelete(friend_list, (FREEFUNC*)FreeXfireFriend);
			}
		}
		else {
			/* Different length of lists */
			retval = XFIH_UNKNOWN;
		}
	}
	else {
		/* Not all lists found */
		retval = XFIH_UNKNOWN;
	}
	/* Delete lists, but not the data in them!
	 * The data is direct pointers to the data in the XfirePacket which
	 * will be deleted along with this data later!
	 */
	LLdelete(username_list, NULL);
	LLdelete(nickname_list, NULL);
	LLdelete(userid_list, NULL);
	
	return retval;
}

int XfireAnalyze_hOnlineStatus(XFINST inst, struct XfirePacket *packet) {
	/* Friends' online status changed
	 * http://xfireplus.com/xfwiki/index.php/Packet_84_00
	 * 
	 * POST-handler only
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT LinkedList*
	#define _XFABP friend_list
	
	LinkedList *userid_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"userid");
	LinkedList *usersid_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"sid");
	
	if (userid_list && usersid_list) {
		if (userid_list->nodes == usersid_list->nodes) {
			ListNode *userid_node = userid_list->first;
			ListNode *usersid_node = usersid_list->first;
			struct XfFriend *xff;
			unsigned int userid;
			TreeNode *online_node;
			LinkedList *friend_list = LLcreate();
			
			while (userid_node) {
				userid = *(unsigned int *)userid_node->x;
				xff = XfireInst_FindUserByUserid(inst, userid);
				if (xff) {
					online_node = BTreeFind(inst->online_people, xff);
					n0sfree(xff->sid);
					xff->sid = n0scopy((non0string*)(usersid_node->x));
					if (online_node) {
						BTreeDel(inst->online_people, online_node);
					}
					if (XfireUtil_IsUserOnline(xff) == 1) {
						BTreeAdd(inst->online_people, xff);
					}
					
					LLaddLast(friend_list, xff);
				}
				else {
					/* For some reason the userid was not found,
					 * mark possible error in library */
					fprintf(stderr, "(XfireAnalyze_hOnlineStatus) Userid %u not found\n", userid);
					retval |= XFIH_NONFATAL;
				}
				
				NEXT_NODE(userid_node);
				NEXT_NODE(usersid_node);
			}
			
			/* include external handlers calling */
			#include "XfirePacketAnalyzeExt.inc"
			
			LLdelete(friend_list, NULL);
		}
		else {
			/* Different length of lists */
			retval = XFIH_UNKNOWN;
		}
	}
	else {
		/* Not all lists found */
		retval = XFIH_UNKNOWN;
	}
	/* Delete lists, but not the data in them!
	 * The data is direct pointers to the data in the XfirePacket which
	 * will be deleted along with this data later!
	 */
	LLdelete(userid_list, NULL);
	LLdelete(usersid_list, NULL);
	
	return retval;
}

int XfireAnalyze_hGameStatus(XFINST inst, struct XfirePacket *packet) {
	/* Friends' game status changed
	 * http://xfireplus.com/xfwiki/index.php/Packet_87_00
	 * 
	 * POST handler!
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT LinkedList*
	#define _XFABP user_list
	
	LinkedList *usersid_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"sid");
	LinkedList *gameid_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"gameid");
	LinkedList *gameip_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"gip");
	LinkedList *gameport_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"gport");
	
	LinkedList *user_list = LLcreate();
	
	if (usersid_list && gameid_list && gameip_list && gameport_list) {
		if (usersid_list->nodes == gameid_list->nodes &&
			gameid_list->nodes == gameip_list->nodes &&
			gameip_list->nodes == gameport_list->nodes) {
			
			/* internal handling first */
			ListNode *usersid_node = usersid_list->first;
			ListNode *gameid_node = gameid_list->first;
			ListNode *gameip_node = gameip_list->first;
			ListNode *gameport_node = gameport_list->first;
			struct XfFriend *xff;
			struct XfGameData *game;
			while (usersid_node) {
				xff = XfireInst_FindUserBySid(inst, (non0string*)usersid_node->x);
				if (xff) {
					game = xff->game;
					game->gameid_old = game->gameid;
					game->ip_old = game->ip;
					game->port_old = game->port;
					game->gameid = *(unsigned int*)gameid_node->x;
					game->ip = *(unsigned int*)gameip_node->x;
					game->port = *(unsigned int*)gameport_node->x;
					if (game->gameid == 0) {
						/* Friend stopped playing game, clear extdata */
						FreeXfireGameDataExt(game->extdata);
						game->extdata = XfireGameDataExt();
					}
					
					LLaddLast(user_list, xff);
				}
				else {
					/* For some reason the user was not found,
					 * mark possible error in library */
					retval |= XFIH_NONFATAL;
				}
				
				NEXT_NODE(usersid_node);
				NEXT_NODE(gameid_node);
				NEXT_NODE(gameip_node);
				NEXT_NODE(gameport_node);
			}
			
			/* include external handlers calling */
			#include "XfirePacketAnalyzeExt.inc"
			
		}
		else {
			/* Different length of lists */
			retval = XFIH_UNKNOWN;
		}
	}
	else {
		/* Not all lists found */
		retval = XFIH_UNKNOWN;
	}
	/* Delete lists, but not the data in them!
	 * The data is direct pointers to the data in the XfirePacket which
	 * will be deleted along with this data later!
	 */
	LLdelete(usersid_list, NULL);
	LLdelete(gameid_list, NULL);
	LLdelete(gameip_list, NULL);
	LLdelete(gameport_list, NULL);
	
	LLdelete(user_list, NULL);
	
	return retval;
}

int XfireAnalyze_hInviteStatus(XFINST inst, struct XfirePacket *packet) {
	/* Server's response to friend invite
	 * http://xfireplus.com/xfwiki/index.php/Packet_89_00
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT non0string*, unsigned int
	#define _XFABP name_value, *result_value
	
	non0string *name_value = XfirePacketGetStringAttribute(packet, (unsigned const char *)"name");
	unsigned int *result_value = XfirePacketGetIntegerAttribute(packet, (unsigned const char *)"result");
	
	if (name_value && result_value) {
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		
		/* no internal handling required */
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Nothing to clear up anymore, name_value is part of the packet,
	 * and will be cleared with the packet later */
	
	return retval;
}

int XfireAnalyze_hFriendInvite(XFINST inst, struct XfirePacket *packet) {
	/* Server's response to friend invite
	 * http://xfireplus.com/xfwiki/index.php/Packet_8a_00
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT LinkedList*
	#define _XFABP invite_list
	
	LinkedList *username_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"name");
	LinkedList *nickname_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"nick");
	LinkedList *msg_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"msg");
	
	LinkedList *invite_list = LLcreate();
	
	if (username_list && nickname_list && msg_list && invite_list) {
		if (username_list->nodes == nickname_list->nodes &&
			nickname_list->nodes == msg_list->nodes) {
			
			ListNode *username_node = username_list->first;
			ListNode *nickname_node = nickname_list->first;
			ListNode *msg_node = msg_list->first;
			
			while(username_node) {
				struct_alloc(invite, XfFriendInvite);
				
				invite->username = (non0string*)username_node->x;
				invite->nickname = (non0string*)nickname_node->x;
				invite->invite_text = (non0string*)msg_node->x;
				
				LLaddLast(invite_list, invite);
				
				NEXT_NODE(username_node);
				NEXT_NODE(nickname_node);
				NEXT_NODE(msg_node);
			}
			/* include external handlers calling */
			#include "XfirePacketAnalyzeExt.inc"
			
			/* no need for internal handling */
		}
		else {
			/* Different length of lists */
			retval = XFIH_UNKNOWN;
		}
	}
	else {
		/* Not all lists found */
		retval = XFIH_UNKNOWN;
	}
	/* Delete lists, but not the data in them!
	 * The data is direct pointers to the data in the XfirePacket which
	 * will be deleted along with this data later!
	 */
	LLdelete(username_list, NULL);
	LLdelete(nickname_list, NULL);
	LLdelete(msg_list, NULL);
	
	LLdelete(invite_list, (FREEFUNC*)free);
	
	return retval;
}

int XfireAnalyze_hDeleteFriend(XFINST inst, struct XfirePacket *packet) {
	/* Removed a friend on friends list
	 * http://xfireplus.com/xfwiki/index.php/Packet_8b_00
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT unsigned int
	#define _XFABP *userid_value
	
	unsigned int *userid_value = XfirePacketGetIntegerAttribute(packet, (unsigned const char *)"userid");
	
	if (userid_value) {
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		
		/* internal handling */
		if (retval & XFIH_HANDLED) {
			struct XfFriend *xff, cmpfriend;
			TreeNode *friendnode;
			cmpfriend.userid = *userid_value;
			friendnode = BTreeFind(inst->friends, &cmpfriend);
			if (friendnode) {
				xff = (struct XfFriend *)friendnode->x;
				BTreeAdd(inst->other_people, xff);
				BTreeDel(inst->friends, friendnode);
			}
			else {
				/* Friend deleted, but not found on structure?? */
				retval |= XFIH_NONFATAL;
			}
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	
	return retval;
}

int XfireAnalyze_hFriendSearch(XFINST inst, struct XfirePacket *packet) {
	/* Results of searching for friends
	 * http://xfireplus.com/xfwiki/index.php/Packet_8f_00
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT LinkedList*
	#define _XFABP result_list
	
	LinkedList *name_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"name");
	LinkedList *fname_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"fname");
	LinkedList *lname_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"lname");
	
	LinkedList *result_list = LLcreate();
	
	if (name_list && fname_list && lname_list) {
		if (name_list->nodes == fname_list->nodes &&
			fname_list->nodes == lname_list->nodes) {
			
			ListNode *name_node = name_list->first;
			ListNode *fname_node = fname_list->first;
			ListNode *lname_node = lname_list->first;
			
			while (name_node) {
				struct_alloc(result, XfFriendSearchResult);
				
				result->name = (non0string*)name_node->x;
				result->fname = (non0string*)fname_node->x;
				result->lname = (non0string*)lname_node->x;
				
				LLaddLast(result_list, result);
				
				NEXT_NODE(name_node);
				NEXT_NODE(fname_node);
				NEXT_NODE(lname_node);
			}
			/* include external handlers calling */
			#include "XfirePacketAnalyzeExt.inc"
			
			/* no internal handling */
		}
		else {
			/* Different list lengths */
			retval = XFIH_UNKNOWN;
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Delete the lists */
	LLdelete(name_list, NULL);
	LLdelete(fname_list, NULL);
	LLdelete(lname_list, NULL);
	
	LLdelete(result_list, (FREEFUNC*)free);
	
	return retval;
}

int XfireAnalyze_hVoiceStatus(XFINST inst, struct XfirePacket *packet) {
	/* Friends' game status changed
	 * http://xfireplus.com/xfwiki/index.php/Packet_93_00
	 *
	 * POST handler
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT LinkedList*
	#define _XFABP user_list
	
	LinkedList *usersid_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"sid");
	LinkedList *gameid_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"vid");
	LinkedList *gameip_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"vip");
	LinkedList *gameport_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"vport");
	
	LinkedList *user_list = LLcreate();
	
	if (usersid_list && gameid_list && gameip_list && gameport_list) {
		if (usersid_list->nodes == gameid_list->nodes && gameid_list->nodes == gameip_list->nodes && gameip_list->nodes == gameport_list->nodes) {
			
			/* internal handling */
			ListNode *usersid_node = usersid_list->first;
			ListNode *gameid_node = gameid_list->first;
			ListNode *gameip_node = gameip_list->first;
			ListNode *gameport_node = gameport_list->first;
			struct XfFriend *xff;
			struct XfGameData *game;
			while (usersid_node) {
				xff = XfireInst_FindUserBySid(inst, (non0string*)usersid_node->x);
				if (xff) {
					game = xff->voice;
					game->gameid_old = game->gameid;
					game->ip_old = game->ip;
					game->port_old = game->port;
					game->gameid = *(unsigned int*)gameid_node->x;
					game->ip = *(unsigned int*)gameip_node->x;
					game->port = *(unsigned int*)gameport_node->x;
					
					LLaddLast(user_list, xff);
				}
				else {
					/* For some reason the user was not found,
					 * mark possible error in library */
					retval |= XFIH_NONFATAL;
				}
				
				NEXT_NODE(usersid_node);
				NEXT_NODE(gameid_node);
				NEXT_NODE(gameip_node);
				NEXT_NODE(gameport_node);
			}
			/* include external handlers calling */
			#include "XfirePacketAnalyzeExt.inc"
		}
		else {
			/* Different length of lists */
			retval = XFIH_UNKNOWN;
		}
	}
	else {
		/* Not all lists found */
		retval = XFIH_UNKNOWN;
	}
	/* Delete lists, but not the data in them!
	 * The data is direct pointers to the data in the XfirePacket which
	 * will be deleted along with this data later!
	 */
	LLdelete(usersid_list, NULL);
	LLdelete(gameid_list, NULL);
	LLdelete(gameip_list, NULL);
	LLdelete(gameport_list, NULL);
	
	LLdelete(user_list, NULL);
	
	return retval;
}

int XfireAnalyze_hGroups(XFINST inst, struct XfirePacket *packet) {
	/* List of custom friend groups
	 * http://xfireplus.com/xfwiki/index.php/Packet_97_00
	 *
	 * SINGLE LIST CALLBACK
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT LinkedList*
	#define _XFABP group_list
	
	LinkedList *groupid_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x19));
	LinkedList *groupname_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x1a));
	
	if (groupid_list && groupname_list) {
		if (groupid_list->nodes == groupname_list->nodes) {
			LinkedList *group_list = LLcreate();
			
			ListNode *idnode = groupid_list->first;
			ListNode *namenode = groupname_list->first;
			struct XfFriendsGroup *xfg;
			while (idnode){
				xfg = XfireFriendsGroup();
				xfg->groupid = *(unsigned int*)idnode->x;
				/* Need to copy the groupname as it is a pointer to the packet data
				 * itself and will be freed when the packet itself is freed */
				xfg->name = n0scopy((non0string*)namenode->x);
				LLaddLast(group_list, xfg);
				
				NEXT_NODE(idnode);
				NEXT_NODE(namenode);
			}
			
			/* include external handlers calling */
			#include "XfirePacketAnalyzeExt.inc"
			
			/* internal handling */
			if (retval & XFIH_HANDLED) {
				ListNode *gnode = group_list->first;
				
				while(gnode) {
					BTreeAdd(inst->groups, gnode->x);
					NEXT_NODE(gnode);
				}
				LLdelete(group_list, NULL);
			}
			else {
				/* free our groups */
				LLdelete(group_list, (FREEFUNC*)FreeXfireFriendsGroup);
			}
		}
		else {
			/* Different length of lists */
			retval = XFIH_UNKNOWN;
		}
	}
	else {
		/* Not all lists found */
		retval = XFIH_UNKNOWN;
	}
	/* Delete lists, but not the data in them!
	 * The data is direct pointers to the data in the XfirePacket which
	 * will be deleted along with this data later!
	 */
	LLdelete(groupname_list, NULL);
	LLdelete(groupid_list, NULL);
	
	return retval;
}

int XfireAnalyze_hInGroups(XFINST inst, struct XfirePacket *packet) {
	/* Assignment of friends in custom friend groups
	 * http://xfireplus.com/xfwiki/index.php/Packet_98_00
	 * 
	 * TODO: POST handler
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT LinkedList*, LinkedList*
	#define _XFABP userid_list, groupid_list
	
	LinkedList *userid_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x01));
	LinkedList *groupid_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x19));
	
	if (groupid_list && userid_list) {
		if (groupid_list->nodes == userid_list->nodes) {
			/* include external handlers calling */
			#include "XfirePacketAnalyzeExt.inc"
			
			/* internal handling */
			if (retval & XFIH_HANDLED) {
				ListNode *uidnode = userid_list->first;
				ListNode *gidnode = groupid_list->first;
				
				while (uidnode){
					struct XfFriend *xff = XfireInst_FindUserByUserid(inst, *(unsigned int*)uidnode->x);
					struct XfFriendsGroup *xfg = XfireInst_FindGroupByGroupid(inst, *(unsigned int*)gidnode->x);
					if (xfg) {
						if (!xff) {
							xff = XfireFriend();
							xff->userid = *(unsigned int*)uidnode->x;
							xff->username = n0salloc(0);
							xff->nickname = n0salloc(0);
							BTreeAdd(inst->other_people, xff);
						}
						LLaddLast(xfg->users, xff);
					}
					else {
						/* Fail... */
						retval |= XFIH_NONFATAL;
					}
					
					NEXT_NODE(uidnode);
					NEXT_NODE(gidnode);
				}
			}
		}
		else {
			/* Different length of lists */
			retval = XFIH_UNKNOWN;
		}
	}
	else {
		/* Not all lists found */
		retval = XFIH_UNKNOWN;
	}
	/* Delete lists, but not the data in them!
	 * The data is direct pointers to the data in the XfirePacket which
	 * will be deleted along with this data later!
	 */
	LLdelete(groupid_list, NULL);
	LLdelete(userid_list, NULL);
	
	return retval;
}

int XfireAnalyze_hGroupCreate(XFINST inst, struct XfirePacket *packet) {
	/* New friend group created
	 * http://xfireplus.com/xfwiki/index.php/Packet_99_00
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT unsigned int, non0string*
	#define _XFABP *groupid, groupname
	
	unsigned int *groupid = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x19));
	non0string *groupname = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x1a));
	
	if (groupid && groupname) {
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		
		/* internal handling */
		if (retval & XFIH_HANDLED) {
			struct XfFriendsGroup *xfg = XfireFriendsGroup();
			xfg->groupid = *groupid;
			/* Need to copy the groupname as it is a pointer to the packet data
			 * itself and will be freed when the packet itself is freed */
			xfg->name = n0scopy(groupname);
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	
	return retval;
}

int XfireAnalyze_hTextStatus(XFINST inst, struct XfirePacket *packet) {
	/* Friends' text status changed
	 * http://xfireplus.com/xfwiki/index.php/Packet_9a_00
	 * 
	 * POST handler
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT LinkedList*
	#define _XFABP user_list
	
	LinkedList *sid_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"sid");
	LinkedList *msg_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"msg");
	
	LinkedList *user_list = LLcreate();
	
	if (sid_list && msg_list) {
		if (sid_list->nodes == msg_list->nodes) {
			/* internal handling */
			ListNode *sid_node = sid_list->first;
			ListNode *msg_node = msg_list->first;
			struct XfFriend *xff;
			while (sid_node) {
				xff = XfireInst_FindUserBySid(inst, (non0string*)sid_node->x);
				if (xff) {
					n0sfree(xff->status);
					xff->status = n0scopy((non0string*)msg_node->x);
					
					LLaddLast(user_list, xff);
				}
				else {
					/* For some reason the user was not found,
					 * mark possible error in library */
					retval |= XFIH_NONFATAL;
				}
				
				NEXT_NODE(sid_node);
				NEXT_NODE(msg_node);
			}
			
			/* include external handlers calling */
			#include "XfirePacketAnalyzeExt.inc"
		}
		else {
			/* Different length of lists */
			retval = XFIH_UNKNOWN;
		}
	}
	else {
		/* Not all lists found */
		retval = XFIH_UNKNOWN;
	}
	/* Delete lists, but not the data in them!
	 * The data is direct pointers to the data in the XfirePacket which
	 * will be deleted along with this data later!
	 */
	LLdelete(sid_list, NULL);
	LLdelete(msg_list, NULL);
	
	LLdelete(user_list, NULL);
	
	return retval;
}

int XfireAnalyze_hGameInfo(XFINST inst, struct XfirePacket *packet) {
	/* Friends' games got extra information
	 * http://xfireplus.com/xfwiki/index.php/Packet_9c_00
	 * 
	 * TODO: POST handler
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT LinkedList*, LinkedList*
	#define _XFABP sid_list, pairs_list
	
	LinkedList *sid_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"sid");
	LinkedList *msg_list = XfirePacketGetListAttribute(packet, (unsigned const char *)"gcd");
	
	if (sid_list && msg_list) {
		if (sid_list->nodes == msg_list->nodes) {
			ListNode *msg_node = msg_list->first;
			LinkedList *pairs_list = LLcreate();
			struct PairedList *split_list;
			
			while (msg_node) {
				split_list = XfireStringPairsSplit((non0string*)msg_node->x);
				LLaddLast(pairs_list, split_list);
				NEXT_NODE(msg_node);
			}
			
			/* include external handlers calling */
			#include "XfirePacketAnalyzeExt.inc"
			
			/* internal handling */
			if (retval & XFIH_HANDLED) {
				struct XfFriend *xff;
				ListNode *sid_node = sid_list->first;
				ListNode *pair_node = pairs_list->first;
				
				while (sid_node) {
					xff = XfireInst_FindUserBySid(inst, (non0string*)sid_node->x);
					if (xff) {
						split_list = (struct PairedList *)pair_node->x;
						LLdelete(xff->game->extdata->names, (FREEFUNC*)n0sfree);
						LLdelete(xff->game->extdata->values, (FREEFUNC*)n0sfree);
						
						xff->game->extdata->names = split_list->names;
						xff->game->extdata->values = split_list->values;
					}
					else {
						/* For some reason the user was not found,
						 * mark possible error in library */
						retval |= XFIH_NONFATAL;
					}
						
					NEXT_NODE(sid_node);
					NEXT_NODE(pair_node);
				}
			}
			
			/* Delete the temporary list along with the PairedList structures,
			 * the LinkedLists inside those structures are now in game extdata */
			LLdelete(pairs_list, (FREEFUNC*)free);
		}
		else {
			/* Different length of lists */
			retval = XFIH_UNKNOWN;
		}
	}
	else {
		/* Not all lists found */
		retval = XFIH_UNKNOWN;
	}
	/* Delete lists, but not the data in them!
	 * The data is direct pointers to the data in the XfirePacket which
	 * will be deleted along with this data later!
	 */
	LLdelete(sid_list, NULL);
	LLdelete(msg_list, NULL);
	
	return retval;
}

int XfireAnalyze_hChangeNick(XFINST inst, struct XfirePacket *packet) {
	/* A friend changed their nickname
	 * http://xfireplus.com/xfwiki/index.php/Packet_a1_00
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT struct XfFriend *, non0string*
	#define _XFABP xff, nickname_value
	
	unsigned int *userid_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x01));
	non0string *nickname_value = XfirePacketGetStringAttribute(packet, SHORT_ATTRIBUTE(\x0d));
	
	if (userid_value && nickname_value) {
		struct XfFriend *xff = XfireInst_FindUserByUserid(inst, *userid_value);
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		
		/* internal handling */
		if (retval & XFIH_HANDLED) {
			if (xff) {
				n0sfree(xff->nickname);
				xff->nickname = n0scopy(nickname_value);
			}
			else {
				/* For some reason the user was not found,
				 * mark possible error in library */
				retval |= XFIH_NONFATAL;
			}
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	
	return retval;
}

int XfireAnalyze_hScreenshots(XFINST inst, struct XfirePacket *packet) {
	/* Screenshots for a user received
	 * http://xfireplus.com/xfwiki/index.php/Packet_ac_00
	 * 
	 * SINGLE LIST CALLBACKS
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT struct XfFriend*, LinkedList*
	#define _XFABP xff, screenshot_list
	
	unsigned int *userid_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x01));
	LinkedList *index_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x5c));
	LinkedList *gameid_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x21));
	LinkedList *gameip_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x22));
	LinkedList *gameport_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x23));
	LinkedList *text_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x54));
	LinkedList *time_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x50));
	LinkedList *guid_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x5e));
	
	if (userid_value && index_list && gameid_list && gameip_list &&
		gameport_list && text_list && time_list && guid_list) {
		if (index_list->nodes == gameid_list->nodes && gameid_list->nodes == gameip_list->nodes &&
			gameip_list->nodes == gameport_list->nodes && gameport_list->nodes == text_list->nodes &&
			text_list->nodes == time_list->nodes && time_list->nodes == guid_list->nodes
		) {
			LinkedList *screenshot_list = LLcreate();
			struct XfFriend *xff = XfireInst_FindUserByUserid(inst, *userid_value);
			
			ListNode *index_node = index_list->first;
			ListNode *gameid_node = gameid_list->first;
			ListNode *gameip_node = gameip_list->first;
			ListNode *gameport_node = gameport_list->first;
			ListNode *text_node = text_list->first;
			ListNode *time_node = time_list->first;
			ListNode *guid_node = guid_list->first;
			struct XfScreenshotInfo *ssi;
			while(index_node) {
				ssi = XfireScreenshotInfo();
				
				ssi->index = *(unsigned int*)index_node->x;
				ssi->game = XfireGameData(
					*(unsigned int*)gameid_node->x,
					*(unsigned int*)gameip_node->x,
					*(unsigned int*)gameport_node->x
				);
				ssi->description = n0scopy((non0string*)text_node->x);
				ssi->timestamp = *(unsigned int*)time_node->x;
				ssi->guid = n0scopy((non0string*)guid_node->x);
				
				LLaddLast(screenshot_list, ssi);
				
				NEXT_NODE(index_node);
				NEXT_NODE(gameid_node);
				NEXT_NODE(gameip_node);
				NEXT_NODE(gameport_node);
				NEXT_NODE(text_node);
				NEXT_NODE(time_node);
				NEXT_NODE(guid_node);
			}
			/* include external handlers calling */
			#include "XfirePacketAnalyzeExt.inc"
			
			/* internal handling */
			if (retval & XFIH_HANDLED) {
				if (xff) {
					struct XfFriendExt *xfe = xff->extdata;
					/* Delete old screenshot info */
					LLdelete(xfe->screenshots, (FREEFUNC*)FreeXfireScreenshotInfo);
					/* Create a new */
					xfe->screenshots = screenshot_list;
				}
				else {
					/* For some reason the user was not found,
					 * mark possible error in library */
					retval |= XFIH_NONFATAL;
				}
			}
			else {
				/* Delete our screenshots then */
				LLdelete(screenshot_list, (FREEFUNC*)FreeXfireScreenshotInfo);
			}
		}
		else {
			/* Different length of lists */
			retval = XFIH_UNKNOWN;
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Delete lists, but not the data in them!
	 * The data is direct pointers to the data in the XfirePacket which
	 * will be deleted along with this data later!
	 */
	LLdelete(index_list, NULL);
	LLdelete(gameid_list, NULL);
	LLdelete(gameip_list, NULL);
	LLdelete(gameport_list, NULL);
	LLdelete(text_list, NULL);
	LLdelete(time_list, NULL);
	LLdelete(guid_list, NULL);
	
	return retval;
}

int XfireAnalyze_hAdvInfoUpdate(XFINST inst, struct XfirePacket *packet) {
	/* Something was changed in a friend's information
	 * http://xfireplus.com/xfwiki/index.php/Packet_ad_00
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT unsigned int
	#define _XFABP *userid_value
	
	unsigned int *userid_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x01));
	
	if (userid_value) {
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		
		/* internal handling */
		if (retval & XFIH_HANDLED) {
			struct XfFriend *xff = XfireInst_FindUserByUserid(inst, *userid_value);
			if (xff) {
				struct XfirePacket *response = XfirePHc_RequestFriendData(*userid_value);
				if (XfireInst_SendPacket(inst, response)==0)
					retval |= XFIH_NONFATAL; /* Quite bad actually... */
			}
			else {
				/* For some reason the user was not found,
				 * mark possible error in library */
				retval |= XFIH_NONFATAL;
			}
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	
	return retval;
}

int XfireAnalyze_hAvatar(XFINST inst, struct XfirePacket *packet) {
	/* Information about avatar
	 * http://xfireplus.com/xfwiki/index.php/Packet_ae_00
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT unsigned int, unsigned int, unsigned int
	#define _XFABP *userid_value, *avatar_type, *avatar_number
	
	unsigned int *userid_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x01));
	unsigned int *avatar_type = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x34));
	unsigned int *avatar_number = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x1f));
	
	if (userid_value && avatar_type && avatar_number) {
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		
		/* internal handling */
		if (retval & XFIH_HANDLED) {
			struct XfFriend *xff = XfireInst_FindUserByUserid(inst, *userid_value);
			if (xff) {
				xff->extdata->avatartype = *avatar_type;
				xff->extdata->avatarnumber = *avatar_number;
			}
			else {
				/* For some reason the user was not found,
				 * mark possible error in library */
				retval |= XFIH_NONFATAL;
			}
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	
	return retval;
}

int XfireAnalyze_hClans(XFINST inst, struct XfirePacket *packet) {
	/* Clan information for a user received
	 * http://xfireplus.com/xfwiki/index.php/Packet_b0_00
	 *
	 * SINGLE LIST CALLBACKS
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT struct XfFriend*, LinkedList*
	#define _XFABP xff, clanmember_list
	
	unsigned int *userid_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x01));
	LinkedList *clanid_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x6c));
	LinkedList *shortname_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x72));
	LinkedList *longname_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x81));
	LinkedList *nickname_list = XfirePacketGetListAttribute(packet, SHORT_ATTRIBUTE(\x6d));
	
	if (userid_value && clanid_list && shortname_list && longname_list && nickname_list) {
		if (clanid_list->nodes == shortname_list->nodes &&
			shortname_list->nodes == longname_list->nodes &&
			longname_list->nodes == nickname_list->nodes
		) {
			LinkedList *clanmember_list = LLcreate();
			struct XfFriend *xff = XfireInst_FindUserByUserid(inst, *userid_value);
			
			ListNode *clanid_node = clanid_list->first;
			ListNode *shortname_node = shortname_list->first;
			ListNode *longname_node = longname_list->first;
			ListNode *nickname_node = nickname_list->first;
			
			struct XfClan *xfc;
			struct XfClanMember xfcmt, *xfcm;
			TreeNode *member_node;
			xfcmt.user = xff;
			
			while(clanid_node) {
				xfc = XfireClan(*(unsigned int*)clanid_node->x);
				xfc->shortname = n0scopy((non0string*)shortname_node->x);
				xfc->longname = n0scopy((non0string*)longname_node->x);
				xfcm = XfireClanMember(xff, n0scopy((non0string*)nickname_node->x), xfc);
				
				LLaddLast(clanmember_list, xfcm);
				
				NEXT_NODE(clanid_node);
				NEXT_NODE(shortname_node);
				NEXT_NODE(longname_node);
				NEXT_NODE(nickname_node);
			}
			
			/* include external handlers calling */
			#include "XfirePacketAnalyzeExt.inc"
			
			/* internal handling */
			if (retval & XFIH_HANDLED) {
				if (xff) {
					struct XfFriendExt *xfe = xff->extdata;
					unsigned int clanid;
					
					ListNode *memberlist_node = clanmember_list->first;
					
					/* Delete old clan info */
					LLdelete(xfe->clans, NULL);
					/* Create a new one and add 'em */
					xfe->clans = LLcreate();
					while(memberlist_node) {
						xfcm = (struct XfClanMember *)memberlist_node->x;
						clanid = xfcm->clan->clanid;
						
						xfc = XfireInst_FindClanByClanid(inst, clanid);
						if (!xfc) {
							/* New clan! */
							xfc = xfcm->clan;
							BTreeAdd(inst->known_clans, xfc);
							BTreeAdd(xfc->members, xfcm);
						}
						else {
							/* Existing clan! */
							FreeXfireClan(xfcm->clan);
							xfcm->clan = xfc;
							member_node = BTreeFind(xfc->members, &xfcmt);
							if (member_node) {
								/* This user was in the clan already... update info*/
								FreeXfireClanMember((struct XfClanMember *)member_node->x);
								member_node->x = xfcm;
							}
							else {
								/* Add the user to the clan roster */
								BTreeAdd(xfc->members, xfcm);
							}
						}
						
						LLaddLast(xfe->clans, xfc);
						
						NEXT_NODE(memberlist_node);
					}
				}
				else {
					/* For some reason the user was not found,
					 * mark possible error in library */
					retval |= XFIH_NONFATAL;
				}
			}
			else {
				/* No internal handling? Free the data then */
				ListNode *memberlist_node = clanmember_list->first;
				while (memberlist_node) {
					xfcm = (struct XfClanMember *)memberlist_node->x;
					FreeXfireClan(xfcm->clan);
					
					NEXT_NODE(memberlist_node);
				}
			}
			LLdelete(clanmember_list, NULL);
		}
		else {
			/* Different length of lists */
			retval = XFIH_UNKNOWN;
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	/* Delete lists, but not the data in them!
	 * The data is direct pointers to the data in the XfirePacket which
	 * will be deleted along with this data later!
	 */
	LLdelete(clanid_list, NULL);
	LLdelete(shortname_list, NULL);
	LLdelete(longname_list, NULL);
	LLdelete(nickname_list, NULL);
	
	return retval;
}

int XfireAnalyze_hEGame(XFINST inst, struct XfirePacket *packet) {
	/* The server reminding that the user is in fact playing a game
	 * http://xfireplus.com/xfwiki/index.php/Packet_b7_00
	 */
	unsigned int retval = XFIH_HANDLED;
	
	/* define external handler function type and calling parameters */
	#define _XFABT unsigned int
	#define _XFABP *gameid_value
	
	unsigned int *gameid_value = XfirePacketGetIntegerAttribute(packet, SHORT_ATTRIBUTE(\x21));
	
	if (gameid_value) {
		/* include external handlers calling */
		#include "XfirePacketAnalyzeExt.inc"
		
		/* internal handling */
		if (retval & XFIH_HANDLED) {
			struct XfirePacket *response = XfirePHc_GameStatus(*gameid_value, 0, 0);
			if (XfireInst_SendPacket(inst, response)==0)
				retval |= XFIH_NONFATAL; /* Quite bad actually... */
		}
	}
	else {
		/* Not all attributes found */
		retval = XFIH_UNKNOWN;
	}
	
	return retval;
}

